package cn.yunbamboos.mybatis.mapping.methods.query;

import cn.hutool.core.util.StrUtil;
import cn.yunbamboos.mybatis.mapping.sqlnode.CustomWhereSqlNode;
import cn.yunbamboos.mybatis.metadata.TableInfo;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.IfSqlNode;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode;
import org.apache.ibatis.session.Configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@SuppressWarnings("DuplicatedCode")
public class QueryByPrimaryKeys extends QueryByPrimaryKey {

    public QueryByPrimaryKeys(Configuration configuration, TableInfo tableInfo) {
        super("queryByPrimaryKeys", configuration, tableInfo);
    }

    @Override
    protected SqlSource getSqlSource() {
        String tableName = BACKTICK + this.tableInfo.getTableName() + BACKTICK;

        List<String> columns = new ArrayList<>();
        List<String> wheres = new ArrayList<>();
        this.tableInfo.getPrimaryKeyList().forEach(pk -> {
            columns.add(BACKTICK + pk.getColumn() + BACKTICK);
            if (wheres.isEmpty()) {
                wheres.add(BACKTICK + pk.getColumn() + BACKTICK + SPACE + EQUALS + SPACE + HASH_LEFT_BRACE + pk.getColumn() + RIGHT_BRACE);
            }
        });
        this.tableInfo.getFieldList().forEach(field -> {
            columns.add(BACKTICK + field.getColumn() + BACKTICK);
        });

        List<SqlNode> contents = new ArrayList<>();
        // select * from table
        contents.add(new StaticTextSqlNode("select " + StrUtil.join(", ", columns) + " from " + tableName));
        // 添加动态条件
        getWhereSqlNode().ifPresent(contents::add);
        SqlNode sqlNode = new MixedSqlNode(contents);
        return new DynamicSqlSource(configuration, sqlNode);
    }

    protected Optional<SqlNode> getWhereSqlNode() {
        List<SqlNode> contents = new ArrayList<>();

        this.tableInfo.getPrimaryKeyList().forEach(pk -> {
            createSqlNode(pk.getPropertyType(), pk.getColumn()).ifPresent(contents::add);
        });

        SqlNode sqlNode = new MixedSqlNode(contents);
        return Optional.of(new CustomWhereSqlNode(configuration, sqlNode));
    }

    private Optional<SqlNode> createSqlNode(Class<?> propertyType, String column) {
        if (String.class.equals(propertyType)) {
            return createSqlNodeString(column);
        } else {
            return createSqlNodeInt(column);
        }
    }

    private Optional<SqlNode> createSqlNodeInt(String column) {
        String text = getText(column);
        String notNullTest = getNotNullTest(column);
        StaticTextSqlNode contents = new StaticTextSqlNode(text);
        return Optional.of(new IfSqlNode(contents, notNullTest));
    }

    private Optional<SqlNode> createSqlNodeString(String column) {
        String text = getText(column);
        String notNullTest = getNotNullTest(column);
        String notEmptyTest = getNotEmptyTest(column);
        StaticTextSqlNode contents = new StaticTextSqlNode(text);
        return Optional.of(new IfSqlNode(contents, notNullTest + SPACE + AND + SPACE + notEmptyTest));
    }

    private String getText(String column) {
        // and `column` = #{conditions.column}
        return SPACE + AND + SPACE + BACKTICK + column + BACKTICK + SPACE + EQUALS + SPACE + HASH_LEFT_BRACE + column + RIGHT_BRACE;
    }

    private String getNotNullTest(String column) {
        return column + SPACE + EXCLAMATION_MARK + EQUALS + SPACE + NULL;
    }

    private String getNotEmptyTest(String column) {
        return column + SPACE + EXCLAMATION_MARK + EQUALS + SPACE + SINGLE_QUOTE + SINGLE_QUOTE;
    }

}
